package com.xbongbong.saas.help.workflow;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.xbongbong.paas.constant.MessageConstant;
import com.xbongbong.paas.domain.entity.ext.PaasFormDataEntityExt;
import com.xbongbong.paas.enums.DelEnum;
import com.xbongbong.paas.field.MultiUnitItemPoJo;
import com.xbongbong.paas.field.UnitItemPoJo;
import com.xbongbong.paas.pojo.ProductFieldPojo;
import com.xbongbong.paas.redis.RedisPrefixConstant;
import com.xbongbong.paas.toolbox.exception.XbbException;
import com.xbongbong.paas.toolbox.util.CollectionsUtil;
import com.xbongbong.paas.toolbox.util.DateUtil;
import com.xbongbong.pro.constant.BusinessConstant;
import com.xbongbong.pro.enums.errorcodes.SystemErrorCodeEnum;
import com.xbongbong.saas.domain.entity.CostAdjustProductEntity;
import com.xbongbong.saas.domain.entity.ProductStockEntity;
import com.xbongbong.saas.domain.entity.ProductStockUpdateEntity;
import com.xbongbong.saas.domain.entity.ProductWarehouseEntity;
import com.xbongbong.saas.enums.SelectProductEnum;
import com.xbongbong.saas.enums.business.ProductEnum;
import com.xbongbong.saas.enums.product.CostAdjustProductEnum;
import com.xbongbong.saas.help.BatchKeyHelp;
import com.xbongbong.saas.help.SaasFormHelp;
import com.xbongbong.saas.help.SaasProductHelp;
import com.xbongbong.saas.model.CostAdjustProductModel;
import com.xbongbong.saas.model.ProductStockModel;
import com.xbongbong.saas.model.ProductWarehouseModel;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * @author WuHB
 * @date 2021/12/23 09:23
 */
@Component
public class CostAdjustHelp {

    private static final Logger LOG = LoggerFactory.getLogger(CostAdjustHelp.class);

    @Resource
    private CostAdjustProductModel costAdjustProductModel;

    @Resource
    private ProductStockModel productStockModel;

    @Resource
    private ProductWarehouseModel productWarehouseModel;

    @Resource
    private WorkflowProductHelp workflowProductHelp;

    @Resource
    private SaasFormHelp saasFormHelp;

    @Resource
    private SaasProductHelp saasProductHelp;

    /**
     * 更新库存操作
     *
     * @param productArray 产品集合
     * @param corpid       公司ID
     * @param dataId       数据ID
     * @param formId       表单ID
     * @throws XbbException 异常
     */
    public void addUpdateStock(JSONArray productArray, String corpid, Long dataId, Long formId) throws XbbException {
        //新增
        List<CostAdjustProductEntity> addCostAdjustProductEntityList = new ArrayList<>();
        // 获取调整单产品信息
        Map<Long, PaasFormDataEntityExt> productMap = saasProductHelp.product2saveMap(corpid, productArray);
        // 获取调整单产品所有仓库信息
        Map<Long, List<ProductWarehouseEntity>> productAllWarehouseMap = this.findProductAllWarehouse(corpid, productArray);
        // 获取调整单产品所有批次信息
        Map<Long, List<ProductStockEntity>> productAllBatchMap = this.findProductAllBatch(corpid, productArray);
        // 需要更新的批次信息
        List<ProductStockUpdateEntity> updateStockList = new ArrayList<>();
        // 需要更新的仓库信息
        List<ProductWarehouseEntity> updateWarehouseList = new ArrayList<>();
        // 需要跟新的产品信息
        Map<Long, ProductStockUpdateEntity> updateProductMap = new HashMap<>();
        List<CostAdjustProductEntity> costAdjustProductEntityList = costAdjustProductModel.listByAdjustId(corpid, dataId, Arrays.asList(DelEnum.NORMAL.getDel()));
        for (int i = 0; i < productArray.size(); i++) {
            JSONObject jsonObject = productArray.getJSONObject(i);
            CostAdjustProductEntity productEntity = costAdjustProductEntityList.stream().filter(item -> Objects.equals(item.getProductId(), jsonObject.getLong(CostAdjustProductEnum.PRODUCT.getAttr()))).findFirst().orElse(null);
            if (Objects.isNull(productEntity)) {
                throw new XbbException(SystemErrorCodeEnum.API_ERROR_100018);
            }
            jsonObject.put(SelectProductEnum.NUM.getAttr(), 0);
            // 是否开启了批次
            Integer enableBatchShelfLife = jsonObject.getInteger(ProductEnum.ENABLE_BATCH_SHELF_LIFE.getAttr());
            // 移除序列号相关数据，不存入关联产品数据中
            ProductFieldPojo productFieldPojo = saasFormHelp.getProductFieldPojo(RedisPrefixConstant.PRODUCT_UNIT, ProductEnum.UNIT.getAttr(), corpid);
            // 产品ID
            Long productId = jsonObject.getLong(CostAdjustProductEnum.PRODUCT.getAttr());
            // 仓库ID
            Long warehouseId = this.getWarehouseId(jsonObject.getJSONArray(CostAdjustProductEnum.WAREHOUSE.getAttr()));
            // 批次Key
            String batchKey = BatchKeyHelp.getBatchKey(productId, warehouseId, jsonObject.getString(CostAdjustProductEnum.BATCH.getAttr()),
                    jsonObject.getLong(CostAdjustProductEnum.PRODUCE_DATE.getAttr()), getGuaranteePeriod(jsonObject.getLong(CostAdjustProductEnum.GUARANTEE_PERIOD.getAttr())));
            // 产品信息
            JSONObject productData = productMap.get(productId).getData();
            // 调整金额
            BigDecimal adjustAmount = jsonObject.getBigDecimal(CostAdjustProductEnum.ADJUST_AMOUNT.getAttr());
            // 获取批次总成本
            List<ProductStockEntity> productStockEntityList = productAllBatchMap.get(productId);
            BigDecimal totalBatchAmount = this.getBatchTotalCost(productStockEntityList, updateStockList, jsonObject, adjustAmount);
            // 获取仓库总成本
            List<ProductWarehouseEntity> productWarehouseEntitieList = productAllWarehouseMap.get(productId);
            if (CollectionsUtil.isEmpty(productWarehouseEntitieList)) {
                throw new XbbException(SystemErrorCodeEnum.API_ERROR_100018, MessageConstant.WAREHOUSE_INFO_NOT_EMPTY, productId);
            }
            BigDecimal totalWarehouseAmount = this.getWarehouseTotalCost(productWarehouseEntitieList, updateWarehouseList, jsonObject, productId, warehouseId, totalBatchAmount, adjustAmount);
            // 计算出产品的总成本是多少并放入待更新集合
            BigDecimal productStock = productData.getBigDecimal(ProductEnum.STOCK.getAttr());
            BigDecimal productCost = totalWarehouseAmount.divide(productStock, 7, RoundingMode.HALF_DOWN);
            if (Objects.isNull(adjustAmount)) {
                throw new XbbException(SystemErrorCodeEnum.API_ERROR_100028);
            }
            ProductStockUpdateEntity productStockUpdateEntity = new ProductStockUpdateEntity(productId,  Double.valueOf(adjustAmount.toString()));
            if (updateProductMap.containsKey(productId)) {
                ProductStockUpdateEntity updateEntity = updateProductMap.get(productId);
                Double cost = updateEntity.getCost();
                updateEntity.setCost(Double.valueOf(BigDecimal.valueOf(cost).add(adjustAmount).toString()));
            } else {
                updateProductMap.put(productId, productStockUpdateEntity);
            }
            // 获取调整单实体
            CostAdjustProductEntity costAdjustProductEntity = this.getAdjustEntity(adjustAmount, batchKey, productData, productId, warehouseId, productStockEntityList, productWarehouseEntitieList);
            costAdjustProductEntity.setCorpid(corpid);
            costAdjustProductEntity.setFormId(formId);
            costAdjustProductEntity.setCostAdjustId(dataId);
            costAdjustProductEntity.setProductId(productId);
            costAdjustProductEntity.setWarehouseId(warehouseId);
            costAdjustProductEntity.setSort(i);
            costAdjustProductEntity.setCostAdjustAmount(adjustAmount);
            String unitId = jsonObject.getString(CostAdjustProductEnum.BASIC_UNIT.getAttr());
            costAdjustProductEntity.setProductUnit(unitId);
            Integer enableMultiUnit = productData.getInteger(ProductEnum.ENABLE_MULTI_UNIT.getAttr());
            if (Objects.nonNull(enableMultiUnit) && Objects.equals(enableMultiUnit, 1)) {
                List<MultiUnitItemPoJo> multiItems = productFieldPojo.getMultiItems();
                MultiUnitItemPoJo multiUnitItemPoJo = null;
                if (CollectionUtils.isNotEmpty(multiItems)) {
                    multiUnitItemPoJo = multiItems.stream().filter(item -> Objects.equals(unitId, String.valueOf(item.getValue()))).findFirst().orElse(null);
                }
                if (Objects.nonNull(multiUnitItemPoJo)) {
                    costAdjustProductEntity.setBusinessUnit(multiUnitItemPoJo.getBaseValue());
                    List<UnitItemPoJo> itemDataPoJoList = multiUnitItemPoJo.getItemDataPoJoList();
                    MultiUnitItemPoJo finalMultiUnitItemPoJo = multiUnitItemPoJo;
                    UnitItemPoJo unitItemPoJo = itemDataPoJoList.stream().filter(item -> Objects.equals(finalMultiUnitItemPoJo.getBaseValue(), item.getValue())).findFirst().orElse(new UnitItemPoJo());
                    costAdjustProductEntity.setRate(unitItemPoJo.getRate());
                    costAdjustProductEntity.setGroupId(multiUnitItemPoJo.getValue());
                    costAdjustProductEntity.setProductUnit(String.valueOf(multiUnitItemPoJo.getBaseValue()));
                }
            } else {
                jsonObject.put(CostAdjustProductEnum.BASIC_UNIT.getAttr(), productData.getString(ProductEnum.UNIT.getAttr()));
            }
            jsonObject.put(CostAdjustProductEnum.STOCK.getAttr(), costAdjustProductEntity.getInventoryNumber());
            jsonObject.put(CostAdjustProductEnum.TOTAL_COST.getAttr(), costAdjustProductEntity.getTotalCost());
            jsonObject.put(BusinessConstant.PRODUCT_SUB_ID, productEntity.getId());
            jsonObject.put(BusinessConstant.REF_PRODUCT_ID, productEntity.getId());
            costAdjustProductEntity.setData(jsonObject);
            addCostAdjustProductEntityList.add(costAdjustProductEntity);
        }
        if (CollectionsUtil.isNotEmpty(updateStockList)) {
            Set<String> batchkeySet = updateStockList.stream().map(ProductStockUpdateEntity::getKey).collect(Collectors.toSet());
            productStockModel.updateStockDB(batchkeySet, updateStockList, corpid);
        }
        productWarehouseModel.updateStockBatch(updateWarehouseList, corpid);
        workflowProductHelp.updateProductStock(updateProductMap, corpid);

    }

    /**
     * 找到产品的所有仓库信息
     *
     * @param corpid      公司ID
     * @param productList 产品信息集合
     * @return 仓库信息 map -> 产品ID value -> 产品的所有仓库信息
     * @throws XbbException 异常
     */
    private Map<Long, List<ProductWarehouseEntity>> findProductAllWarehouse(String corpid, JSONArray productList) throws XbbException {
        if (CollectionsUtil.isEmpty(productList)) {
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100002, MessageConstant.PRODUCT_INFO_NOT_EMPTY);
        }
        List<Long> productIdList = this.getProductIdList(productList);
        List<ProductWarehouseEntity> productWarehouseList = productWarehouseModel.getIdList(corpid, productIdList);
        Map<Long, List<ProductWarehouseEntity>> productWarehouseMap = new HashMap<>();
        for (ProductWarehouseEntity entity : productWarehouseList) {
            Long productId = entity.getProductId();
            List<ProductWarehouseEntity> value = productWarehouseMap.getOrDefault(productId, new ArrayList<>());
            value.add(entity);
            productWarehouseMap.put(productId, value);
        }
        return productWarehouseMap;
    }

    /**
     * 找到存在批次的产品
     *
     * @param corpid      公司ID
     * @param productList 产品ID集合
     * @return 产品批次信息
     * @throws XbbException 异常
     */
    private Map<Long, List<ProductStockEntity>> findProductAllBatch(String corpid, JSONArray productList) throws XbbException {
        if (CollectionsUtil.isEmpty(productList)) {
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100002, MessageConstant.PRODUCT_INFO_NOT_EMPTY);
        }
        List<Long> productIdList = this.getProductIdList(productList);
        List<Long> warehouseIdList = new ArrayList<>();
        for (int i = 0; i < productList.size(); i++) {
            JSONObject productJson = productList.getJSONObject(i);
            warehouseIdList.add(this.getWarehouseId(productJson.getJSONArray(CostAdjustProductEnum.WAREHOUSE.getAttr())));
        }
        List<ProductStockEntity> productStockEntityList = productStockModel.listByProductIdIn(corpid, productIdList, warehouseIdList);
        Map<Long, List<ProductStockEntity>> productStockMap = new HashMap<>();
        for (ProductStockEntity productStockEntity : productStockEntityList) {
            Long productId = productStockEntity.getProductId();
            List<ProductStockEntity> productStockList = productStockMap.getOrDefault(productId, new ArrayList<>());
            productStockList.add(productStockEntity);
            productStockMap.put(productId, productStockList);
        }
        return productStockMap;
    }

    /**
     * 获取产品ID集合
     *
     * @param productList 产品信息
     * @return 产品ID集合
     * @throws XbbException 异常
     */
    private List<Long> getProductIdList(JSONArray productList) throws XbbException {
        List<Long> productIdList = new ArrayList<>(productList.size());
        for (int i = 0; i < productList.size(); i++) {
            JSONObject products = productList.getJSONObject(i);
            Long productId = products.getLong(CostAdjustProductEnum.PRODUCT.getAttr());
            if (Objects.isNull(productId)) {
                throw new XbbException(SystemErrorCodeEnum.API_ERROR_100002, MessageConstant.PRODUCT_INFO_NOT_EMPTY);
            }
            productIdList.add(productId);
        }
        return productIdList;
    }

    /**
     * 获取批次总成本
     *
     * @param productStockEntityList 产品批次信息
     * @param jsonObject             产品信息
     * @param adjustAmount           调整金额
     * @return 批次总成本
     */
    private BigDecimal getBatchTotalCost(List<ProductStockEntity> productStockEntityList, List<ProductStockUpdateEntity> updateStockList, JSONObject jsonObject, BigDecimal adjustAmount) throws XbbException {
        BigDecimal totalBatchAmount = BigDecimal.ZERO;
        if (!Objects.equals(jsonObject.getInteger(ProductEnum.ENABLE_BATCH_SHELF_LIFE.getSaasAttr()), 1) || CollectionsUtil.isEmpty(productStockEntityList)) {
            return totalBatchAmount;
        }
        Long productId = jsonObject.getLong(CostAdjustProductEnum.PRODUCT.getAttr());
        Long warehouseId = this.getWarehouseId(jsonObject.getJSONArray(CostAdjustProductEnum.WAREHOUSE.getAttr()));
        String batchKey = BatchKeyHelp.getBatchKey(productId, warehouseId, jsonObject.getString(CostAdjustProductEnum.BATCH.getAttr()),
                jsonObject.getLong(CostAdjustProductEnum.PRODUCE_DATE.getAttr()), getGuaranteePeriod(jsonObject.getLong(CostAdjustProductEnum.GUARANTEE_PERIOD.getAttr())));
        for (ProductStockEntity stockEntity : productStockEntityList) {
            BigDecimal totalCost = BigDecimal.valueOf(Objects.isNull(stockEntity.getTotalCost()) ? 0D : stockEntity.getTotalCost());
            if (Objects.equals(batchKey, stockEntity.getBatchKey())) {
                totalCost = totalCost.add(adjustAmount);
                stockEntity.setTotalCost(totalCost.doubleValue());
                stockEntity.setCost(totalCost.divide(BigDecimal.valueOf(stockEntity.getNum()), 7, RoundingMode.HALF_DOWN).doubleValue());
                ProductStockUpdateEntity stockUpdateEntity = new ProductStockUpdateEntity(stockEntity.getProductId(), adjustAmount.doubleValue());
                stockUpdateEntity.setKey(stockEntity.getBatchKey());
                updateStockList.add(stockUpdateEntity);
                jsonObject.put(BusinessConstant.ADJUST_BATCH_STOCK, stockEntity.getNum());
                jsonObject.put(BusinessConstant.ADJUST_BATCH_COST, stockEntity.getCost());
                jsonObject.put(BusinessConstant.ADJUST_AMOUNT, adjustAmount);
            }
            totalBatchAmount = totalBatchAmount.add(totalCost);
        }
        return totalBatchAmount;
    }

    /**
     * 获取仓库总成本
     *
     * @param productWarehouseList 产品仓库信息
     * @param totalBatchAmount     批次总成本
     * @param adjustAmount         调整金额
     * @return 批次总成本
     */
    private BigDecimal getWarehouseTotalCost(List<ProductWarehouseEntity> productWarehouseList, List<ProductWarehouseEntity> updateWarehouseList, JSONObject jsonObject,
                                             Long productId, Long warehouseId, BigDecimal totalBatchAmount, BigDecimal adjustAmount) {
        BigDecimal totalWarehouseAmount = BigDecimal.ZERO;
        Integer enableBatchShelfLife = jsonObject.getInteger(ProductEnum.ENABLE_BATCH_SHELF_LIFE.getAttr());
        for (ProductWarehouseEntity warehouseEntity : productWarehouseList) {
            BigDecimal totalCost = BigDecimal.valueOf(Objects.isNull(warehouseEntity.getTotalCost()) ? 0D : warehouseEntity.getTotalCost());
            if (Objects.equals(warehouseEntity.getProductId(), productId) && Objects.equals(warehouseEntity.getWarehouseId(), warehouseId)) {
                totalCost = Objects.equals(enableBatchShelfLife, 1) ? totalBatchAmount : totalCost.add(adjustAmount);
                warehouseEntity.setTotalCost(totalCost.doubleValue());
                warehouseEntity.setCost(totalCost.divide(BigDecimal.valueOf(warehouseEntity.getNum()), 7, RoundingMode.HALF_DOWN).doubleValue());
                updateWarehouseList.add(warehouseEntity);
                jsonObject.put(BusinessConstant.ADJUST_WAREHOUSE_STOCK, warehouseEntity.getNum());
                jsonObject.put(BusinessConstant.ADJUST_WAREHOUSE_COST, warehouseEntity.getCost());
                jsonObject.put(BusinessConstant.ADJUST_AMOUNT, adjustAmount);
            }
            totalWarehouseAmount = totalWarehouseAmount.add(totalCost);
        }
        return totalWarehouseAmount;
    }

    /**
     * 获取调整单实体
     *
     * @param adjustAmount                调整
     * @param batchKey                    批次
     * @param productData                 产品信息
     * @param productStockEntityList      产品批次信息
     * @param productWarehouseEntitieList 产品仓库信息
     * @return 调整单实体
     */
    private CostAdjustProductEntity getAdjustEntity(BigDecimal adjustAmount, String batchKey, JSONObject productData, Long productId, Long warehouseId,
                                                    List<ProductStockEntity> productStockEntityList, List<ProductWarehouseEntity> productWarehouseEntitieList) {
        CostAdjustProductEntity costAdjustProductEntity = new CostAdjustProductEntity();
        int now = DateUtil.getInt();
        BigDecimal num;
        BigDecimal newCost;
        BigDecimal totalCost;
        BigDecimal oldCost;
        String batch = "";
        ProductStockEntity productStockEntity = null;
        Integer enableBatchShelfLife = productData.getInteger(ProductEnum.ENABLE_BATCH_SHELF_LIFE.getAttr());
        // 判断产品是否存在批次
        if (CollectionsUtil.isNotEmpty(productStockEntityList) && Objects.equals(enableBatchShelfLife, 1)) {
            productStockEntity = productStockEntityList.stream().filter(entity -> Objects.equals(entity.getBatchKey(), batchKey)).findFirst().orElse(null);
        }
        if (Objects.nonNull(productStockEntity)) {
            costAdjustProductEntity.setProduceDate(productStockEntity.getProduceDate());
            costAdjustProductEntity.setGuaranteePeriod(productStockEntity.getGuaranteePeriod());
            num = BigDecimal.valueOf(productStockEntity.getNum());
            newCost = BigDecimal.valueOf(productStockEntity.getCost());
            totalCost = BigDecimal.valueOf(productStockEntity.getTotalCost());
            oldCost = totalCost.subtract(adjustAmount).divide(num, 7, RoundingMode.HALF_DOWN);
            batch = productStockEntity.getBatch();
        } else {
            ProductWarehouseEntity productWarehouseEntity = productWarehouseEntitieList.stream().filter(entity -> Objects.equals(entity.getProductId(), productId) && Objects.equals(entity.getWarehouseId(), warehouseId)).findFirst().orElse(new ProductWarehouseEntity());
            num = BigDecimal.valueOf(productWarehouseEntity.getNum());
            newCost = BigDecimal.valueOf(productWarehouseEntity.getCost());
            totalCost = BigDecimal.valueOf(productWarehouseEntity.getTotalCost());
            oldCost = totalCost.subtract(adjustAmount).divide(num, 7, RoundingMode.HALF_DOWN);
        }
        costAdjustProductEntity.setInventoryNumber(num);
        costAdjustProductEntity.setOldCost(oldCost);
        costAdjustProductEntity.setNewCost(newCost);
        costAdjustProductEntity.setTotalCost(totalCost);
        costAdjustProductEntity.setParentId(productData.getLong(ProductEnum.PARENT_ID.getAttr()));
        costAdjustProductEntity.setProductName(productData.getString(ProductEnum.NAME.getAttr()));
        costAdjustProductEntity.setProductPicture(productData.getString(ProductEnum.PRODUCT_IMGS.getAttr()));
        costAdjustProductEntity.setProductNo(productData.getString(ProductEnum.PRODUCT_NO.getAttr()));
        costAdjustProductEntity.setProductSpecification(productData.getString(ProductEnum.SPECIFICATION.getAttr()));
        costAdjustProductEntity.setBatch(batch);
        costAdjustProductEntity.setAddTime(now);
        costAdjustProductEntity.setUpdateTime(now);
        costAdjustProductEntity.setDel(0);
        return costAdjustProductEntity;
    }

    private Long getGuaranteePeriod(Long guaranteePeriod) {
        if (Objects.isNull(guaranteePeriod)) {
            return 0L;
        }
        return guaranteePeriod * 86400;
    }

    /**
     * 获取仓库ID
     *
     * @param jsonArray 仓库信息
     * @return 仓库ID
     * @throws XbbException 异常
     */
    private Long getWarehouseId(JSONArray jsonArray) throws XbbException {
        if (CollectionsUtil.isEmpty(jsonArray)) {
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100002, MessageConstant.WAREHOUSE_INFO_NOT_EMPTY);
        }
        JSONObject warehouseInfo = jsonArray.getJSONObject(0);
        if (MapUtils.isEmpty(warehouseInfo)) {
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100002, MessageConstant.WAREHOUSE_INFO_NOT_EMPTY);
        }
        String warehouseName = warehouseInfo.getString("name");
        Long warehouseId = warehouseInfo.getLong("id");
        if (Objects.isNull(warehouseId)) {
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100002, MessageConstant.WAREHOUSE_INFO_NOT_EMPTY, warehouseName);
        }
        return warehouseId;
    }
}
